%%%---------------------------------------------------------------------------------------
%%% @author    Stuart Jackson <sjackson@simpleenigma.com> [http://erlsoft.org]
%%% @copyright 2006 - 2008 Simple Enigma, Inc. All Rights Reserved.
%%% @doc       EIF AWS Utility Libraray
%%% @reference See <a href="http://erlsoft.org/modules/eifaws" target="_top">Erlang Software Framework</a> for more information
%%% @reference See <a href="http://eifaws.googlecode.com" target="_top">ErlMail Google Code Repository</a> for more information
%%% @version   0.0.1
%%% @since     0.0.1
%%% @end
%%%
%%%
%%% The MIT License
%%%
%%% Copyright (c) 2008 Stuart Jackson, Simple Enigma, Inc. All Righs Reserved
%%%
%%% Permission is hereby granted, free of charge, to any person obtaining a copy
%%% of this software and associated documentation files (the "Software"), to deal
%%% in the Software without restriction, including without limitation the rights
%%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
%%% copies of the Software, and to permit persons to whom the Software is
%%% furnished to do so, subject to the following conditions:
%%%
%%% The above copyright notice and this permission notice shall be included in
%%% all copies or substantial portions of the Software.
%%%
%%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
%%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
%%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
%%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
%%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
%%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
%%% THE SOFTWARE.
%%%
%%%
%%%---------------------------------------------------------------------------------------

-module(eifaws_util).
-author('sjackson@simpleenigma.com').
-include("../include/eifaws.hrl").
-include_lib("xmerl/include/xmerl.hrl").

-define(APPLICATION,eifaws).

-export([escape/1,iso_8601_fmt/0,iso_8601_fmt/1]).
-export([request/1,get_server/1]).
-export([xml_parse/1]).
-export([get_account/0,get_account/1]).
-export([get_app_env/1,get_app_env/2]).
-export([to_xml/1,xml_value/1]).
-export([to_atom/1]).
-export([param_list/2]).
-export([aws_atom/1,aws_atom/2,aws_string/2,aws_integer/2,xml_extract/2,xml_list/2,parse_error/3]).






xml_extract(XMLRoot,List) ->
	Items = lists:foldl(fun({Type,XPath},Acc) -> 
		Value = case Type of
			[] -> [];
			atom    -> eifaws_util:aws_atom(XMLRoot,XPath);
			integer -> eifaws_util:aws_integer(XMLRoot,XPath);
			_       -> eifaws_util:aws_string(XMLRoot,XPath)
		end,
		[Value|Acc]
	end,[],List),
	lists:reverse(Items).


xml_list(XMLRoot,XPath) -> xmerl_xpath:string(XPath,XMLRoot).







aws_atom(XMLRoot) -> aws_atom(XMLRoot,"return").
aws_atom(XMLRoot,XPath) ->
	case xmerl_xpath:string(XPath,XMLRoot) of
		[] -> [];
		Return -> list_to_atom(eifaws_util:xml_value(Return))
	end.

%aws_string(XMLRoot) -> aws_string(XMLRoot,"return").
aws_string(XMLRoot,XPath) ->
	Return = xmerl_xpath:string(XPath,XMLRoot),
	eifaws_util:xml_value(Return).

aws_integer(XMLRoot,XPath) ->
	case xmerl_xpath:string(XPath,XMLRoot) of
		[] -> [];
		Return -> list_to_integer(eifaws_util:xml_value(Return))
	end.



parse_error(Type,HttpCode,ErrorMessage) ->
	XMLRoot = eifaws_util:to_xml(ErrorMessage),
	RequestId = eifaws_util:xml_value(xmerl_xpath:string("RequestID",XMLRoot)),
	Errors = xmerl_xpath:string("Errors/Error",XMLRoot),
	lists:map(fun(E) ->
		Code = eifaws_util:xml_value(xmerl_xpath:string("Code",E)),
		Message = eifaws_util:xml_value(xmerl_xpath:string("Message",E)),
		#ec2_error{type=Type,http_code=HttpCode,code=Code,message=Message,request_id=RequestId}
	end,Errors).








param_list(Key,List) -> param_list(Key,1,List,[]).


param_list(_Key,_Count,[],Acc) -> lists:reverse(Acc);
param_list(Key,Count,[H|T],Acc) ->
	KeyCount = list_to_atom(atom_to_list(Key) ++ "." ++ integer_to_list(Count)),
	param_list(Key,Count+1,T,[{KeyCount,H}|Acc]).






request(URL) ->
	
	case http:request(URL) of
		{ok,{{_Version, Code, _ReasonPhrase}, _Headers, Body}}
			when Code >= 200, Code =< 299 -> 
				?DUMP("/sfe/ec2/samples/message.xml",Body),
				{ok,Body};
		{ok,{{_Version, Code, _ReasonPhrase}, _Headers, Body}}
			when Code >= 400, Code =< 499 -> 
				?DUMP("/sfe/ec2/samples/client_error.xml",Body),
				{error,client,Code,Body};
		{ok,{{_Version, Code, _ReasonPhrase}, _Headers, Body}}
			when Code >= 500, Code =< 599 -> 
				?DUMP("/sfe/ec2/samples/server_error.xml",Body),
				{error,server,Code,Body};
		_ -> {error,unknown}
	end.

xml_parse(Body) -> xmerl_scan:string(Body).




get_server(s3) ->
	case eifaws_util:get_app_env(connection_type) of
		ssl -> ?AWS_EC2_SSL;
		_   -> ?AWS_EC2_URL
	end;
get_server(sdb) ->
	case eifaws_util:get_app_env(connection_type) of
		ssl -> ?AWS_EC2_SSL;
		_   -> ?AWS_EC2_URL
	end;
get_server(sqs) ->
	case eifaws_util:get_app_env(connection_type) of
		ssl -> ?AWS_EC2_SSL;
		_   -> ?AWS_EC2_URL
	end;
get_server(ec2) ->
	case eifaws_util:get_app_env(connection_type) of
		ssl -> ?AWS_EC2_SSL;
		_   -> ?AWS_EC2_URL
	end.





%%-------------------------------------------------------------------------
%% @spec () -> string()
%% @doc  Gets Universal Time Code and formats it into a ISO 8601 String.
%% @end
%%-------------------------------------------------------------------------
iso_8601_fmt() -> iso_8601_fmt(calendar:universal_time()).
iso_8601_fmt(UTC) ->
	{{Year,Month,Day},{Hour,Min,Sec}} = UTC,
	TS = io_lib:format(
		"~4.10.0B-~2.10.0B-~2.10.0BT~2.10.0B:~2.10.0B:~2.10.0BZ",
		[Year, Month, Day, Hour, Min, Sec]),
	lists:flatten(TS).





escape(Integer) when is_integer(Integer) -> escape(integer_to_list(Integer),[]);
escape(String) when is_list(String) -> escape(String,[]).

escape([],Acc) -> lists:flatten(lists:reverse(Acc));

escape([32|T],Acc) ->
	escape(T, [$+|Acc]);
escape([H|T],Acc) when 
			H >= $0, H =< $9;
			H >= $a, H =< $z;
			H >= $A, H =< $Z;
			H == $-; 
			H == $_; 
			H == $. ->
	escape(T, [H|Acc]);
escape([H|T],Acc) ->
	escape(T, [io_lib:format("~.16B",[H]), $% | Acc]).




get_account() -> 
	Accounts = get_app_env(accounts),
	lists:map(fun(A) ->
		account_to_record(A)
	end,Accounts).
get_account(AccountName) ->
	Accounts = get_app_env(accounts),
	case lists:keysearch(AccountName,1,Accounts) of
		{value,Account} -> account_to_record(Account);
		_ -> undefined
	end.


account_to_record({Name,Options}) -> account_to_record(Options,#aws_info{name=Name}).

account_to_record([],AWSInfo) -> AWSInfo;
account_to_record([{aws_access_key,AccessKey}|T],AWSInfo) -> 
	account_to_record(T,AWSInfo#aws_info{aws_access_key=AccessKey});
account_to_record([{aws_secret_key_id,SecretKeyId}|T],AWSInfo) -> 
	account_to_record(T,AWSInfo#aws_info{aws_secret_key_id=SecretKeyId});
account_to_record([{aws_account_number,AccountNumber}|T],AWSInfo) -> 
	account_to_record(T,AWSInfo#aws_info{aws_account_number=AccountNumber});
account_to_record([{cert,Cert}|T],AWSInfo) -> 
	account_to_record(T,AWSInfo#aws_info{cert=Cert});
account_to_record([{private_key,PrivateKey}|T],AWSInfo) -> 
	account_to_record(T,AWSInfo#aws_info{private_key=PrivateKey});
account_to_record([_|T],AWSInfo) -> account_to_record(T,AWSInfo).
	



get_app_env(Opt) -> get_app_env(Opt,[]).
get_app_env(Opt, Default) ->
	case lists:keysearch(?APPLICATION, 1, application:loaded_applications()) of
		false -> application:load(?APPLICATION);
		_ -> ok
	end,
	case application:get_env(?APPLICATION, Opt) of
	{ok, Val} -> Val;
	_ ->
		case init:get_argument(Opt) of
		[[Val | _]] -> Val;
		error		-> Default
		end
	end.


to_atom(List) when is_list(List) -> list_to_atom(List);
to_atom(Atom) when is_atom(Atom) -> Atom.





to_xml(Message) ->
	case xmerl_scan:string(Message) of
		{XMLRoot,_} -> XMLRoot;
		_ -> error
	end.

xml_value([]) -> [];
xml_value(List) when is_list(List) ->
	[NewE] = List,
	xml_value(NewE);
xml_value(Element) when is_record(Element,xmlElement) ->
	Text = case Element#xmlElement.content of
		[] -> [];
		[T] -> T
	end,
	xml_value(Text);
xml_value(Text) when is_record(Text,xmlText) ->
	Text#xmlText.value.








